﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using LightManager.Effects;
using LightManager.UserControls.UnitEditor;

namespace LightManager.UserControls
{
    internal partial class CommandLineControl : UserControl
    {
        private string Css { get; set; }

        public CommandLineControl()
        {
            InitializeComponent();
            this.webBrowser.DocumentText = "";
            this.History = new List<string>();
        }

        public void Initialize(GameManager manager)
        {
            this.Manager = manager;
            this.Manager.LogNeeded += this.Log;

            this.Css = System.IO.File.ReadAllText(System.IO.Path.Combine(Application.StartupPath, "console.css"), Encoding.UTF8);
            this.Css = string.Format("<style>{0}</style>", this.Css);
            this.ClearLog();

            this.Updater = new System.Timers.Timer(500);
            this.Updater.Elapsed += this.Updater_Elapsed;
            this.Updater.Start();
        }

        private string Html { get; set; } //testo scritto
        private bool Synched { get; set; } //true se il browser è sincronizzato con la proprietà Html
        private System.Timers.Timer Updater { get; set; } //timer che si occupa di tenere sincronizzati web browser e proprietà Html

        private List<string> History { get; set; }

        private void ClearLog()
        {
            this.Html = this.Css;
            this.Synched = false;
        }

        private GameManager Manager { get; set; }

        void Log(string text)
        {
            if (string.IsNullOrEmpty(text)) return;
            this.Html = this.Css + string.Format("<font class='time'>{0}: </font>{1}<br />", DateTime.Now.ToString("HH:mm"), text) + this.Html;
            this.Synched = false;
            //this.BeginInvoke(new GenericDelegate<string>(this.LogAsync), text);
        }

        void Updater_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (this.Synched) return;
            try
            {
                this.webBrowser.DocumentText = this.Html;
                this.Synched = true;
            }
            catch { }
        }

        private void textBox_KeyDown(object sender, KeyEventArgs e)
        {
            this.textBox.Text = this.textBox.Text.Replace("\r\n", "");
            if (e.KeyCode == Keys.Enter)
            {
                if (string.IsNullOrEmpty(this.textBox.Text)) return;
                this.BeginInvoke(new GenericDelegate<string>(this.ParseCommand), this.textBox.Text);
                this.History.Add(this.textBox.Text);
                if (this.History.Count >= 50) this.History.RemoveAt(0);
                this.textBox.Text = "";
                this.textBox.Select(0, 0);
            }
            if (e.KeyCode == Keys.Up)
            {
                 if (this.History.Count == 0) return;
                string last = this.History[this.History.Count - 1];
                if (this.History.Contains(this.textBox.Text))
                    if (this.History.IndexOf(this.textBox.Text) > 0)
                        last = this.History[this.History.IndexOf(this.textBox.Text) - 1];
                this.textBox.Text = last;
                this.textBox.SelectAll();
           }        }

        private void ParseCommand(string text)
        {
            text = text.Replace("\r\n", "");
            try
            {
                Action action = null;

                List<string> splitted = new List<string>(text.Split(' '));

                Unit from = this.GetTarget(splitted[0]);
                if (from != null)
                    splitted.RemoveAt(0);

                switch (splitted[0].ToLower())
                {
                    case "damage":
                        splitted.RemoveAt(0);
                        action = this.ParseDamageCommand(splitted);
                        break;
                    case "heal":
                        splitted.RemoveAt(0);
                        action = this.ParseHealCommand(splitted);
                        break;
                    case "status":
                        splitted.RemoveAt(0);
                        action = this.ParseStatusCommand(splitted);
                        break;
                    case "action":
                        splitted.RemoveAt(0);
                        action = this.ParseActionCommand(splitted);
                        break;
                    case "armor":
                        splitted.RemoveAt(0);
                        action = this.ParseArmorCommand(splitted);
                        break;
                    case "shield":
                        splitted.RemoveAt(0);
                        action = this.ParseShieldCommand(splitted);
                        break;
                    case "edit":
                        splitted.RemoveAt(0);
                        this.ParseEditCommand(splitted, true);
                        break;
                    case "create":
                        splitted.RemoveAt(0);
                        this.ParseEditCommand(splitted, false);
                        break;
                    case "destroy":
                        splitted.RemoveAt(0);
                        this.ParseDestroyCommand(splitted);
                        break;
                    case "turn":
                        this.Manager.NextTurn();
                        break;
                    case "victory":
                        this.BattleEnd();
                        break;
                    case "clear":
                        this.ClearLog();
                        break;
                    default:
                        throw new ConsoleException(string.Format("\"{0}\"? non capisco.", splitted[0]));
                }
                if (action != null)
                {
                    action.From = (from != null) ? from.Name.ToString() : "";
                    this.Manager.RegisterAction(action);
                    //if (action.Duration > 1)
                    //    this.Log(string.Format("<font class='details'>l'azione su {0} prosegue per {1} turni</font>", action.Target.Name, action.Duration - 1));
                }
                
            }
            catch (ConsoleException ex)
            {
                this.Log(string.Format("<font class='error'>{0}</font>", ex.Message));
            }
            catch (Exception)
            {
                this.Log(string.Format("<font class='error'>qualcosa non va...</font>"));
            }
        }

        private void BattleEnd()
        {
            DialogResult result = MessageBox.Show("Il comando completa la battaglia.\r\nGli antagonisti e gli effetti relativi alla battaglia saranno rimossi.", "Battle over", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);

            if (result != DialogResult.OK) return;
            this.Manager.EndFight();
        }

        private void ParseEditCommand(List<string> command, bool edit)
        {
            using (UnitEditorForm form = new UnitEditorForm())
            {
                form.NameValidation += this.UnitEditorForm_NameValidation;
                if (edit)
                {
                    Unit target = this.GetTarget(command[0], "damage");
                    form.EditUnit(target);
                    this.Manager.NotifyUnitChanged(target);
                    return;
                }
                Unit item = form.CreateUnit((command.Count > 0) ? command[0] : "");
                if (item == null) return;
                this.Manager.Units.Add(item);
            }
        }

        void UnitEditorForm_NameValidation(object sender, UnitEditor.ValidationEventArgs e)
        {
            Unit unit = (Unit)sender;
            IEnumerable<Unit> sameName = from Unit u in this.Manager.Units
                                         where u.Name.ToLower() == ((string)e.Value).ToLower()
                                         where u.Id != unit.Id select u;
            if (!((string)e.Value).Contains(' ')) //non contiene spazi
                if (sameName.Count() == 0) //non è un nome già in uso
                {
                    e.IsValid = true;
                    return;
                }
            e.IsValid = false;
        }

        private void ParseDestroyCommand(List<string> command)
        {
            Unit target = this.GetTarget(command[0], "destroy");
            DialogResult result = MessageBox.Show(string.Format("Rimuovere la pedina \"{0}\"?", target.Name), "Unit destroy", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
            if (result != DialogResult.OK) return;
            this.Manager.RemoveUnit(target);
        }

        private Action ParseDamageCommand(List<string> command)
        {
            DamageEffect damage = new DamageEffect();
            Action result = new Action(damage);

            result.Duration = this.GetDuration(command);
            //if (result.Duration > 0)
            //    result.Duration--;

            //niente armatura
            for (int i = command.Count - 1; i >= 0; i--)
            {
                string arg = command[i];
                if (arg.ToLower() != "no") continue;
                damage.IgnoreArmor = true;
                command.RemoveAt(i);
            }

            result.Target = this.GetTarget(command[0], "damage");

            for (int i = 1; i < command.Count; i++)
            {
                string dmg = command[i];
                if (dmg.StartsWith("x")) continue;
                bool aggravated = false;
                if (dmg.Contains('a'))
                {
                    aggravated = true;
                    dmg = dmg.Substring(0, dmg.Length - 1);
                }
                DamageSources source = DamageSources.Phisical;
                if (i + 1 < command.Count)
                    if (command[i + 1].ToCharArray()[0] > '9')
                    {
                        Enum.TryParse<DamageSources>(command[i + 1], true, out source);
                        i++;
                    }

                damage.Damages.Add(new Damage() { Number = Convert.ToInt32(dmg), Soruce = source, Aggravate = aggravated });
            }

            return result;
        }

        private Action ParseHealCommand(List<string> command)
        {
            HealingEffect heal = new HealingEffect();
            Action result = new Action(heal);

            result.Duration = this.GetDuration(command);
            //if (result.Duration > 0)
            //    result.Duration--;

            result.Target = this.GetTarget(command[0], "heal");

            string points = command[1];
            bool aggravated = false;
            if (points.Contains('a'))
            {
                aggravated = true;
                points = points.Substring(0, points.Length - 1);
            }

            heal.Aggravated = aggravated;
            heal.Number = Convert.ToInt32(points);

            return result;
        }

        private Action ParseArmorCommand(List<string> command)
        {
            ArmorEffect armor = new ArmorEffect();
            Action result = new Action(armor);

            result.Duration = this.GetDuration(command);
            armor.IsTemporary = (result.Duration > 0);

            result.Target = this.GetTarget(command[0], "armor");

            armor.Type = this.GetAlterEffect(command[1], "armor");

            string points = command[2];
            armor.Ammount = Convert.ToInt32(points);

            DamageSources source = DamageSources.Phisical;
            if (command.Count > 3)
                if (command[3].ToCharArray()[0] > '9')
                    Enum.TryParse<DamageSources>(command[3], true, out source);
            armor.Source = source;

            return result;
        }

        private Action ParseShieldCommand(List<string> command)
        {
            ShieldEffect shield = new ShieldEffect();
            Action result = new Action(shield);

            result.Duration = this.GetDuration(command);
            shield.IsTemporary = (result.Duration > 0);

            result.Target = this.GetTarget(command[0], "armor");

            shield.Type = this.GetAlterEffect(command[1], "armor");

            string points = command[2];
            shield.Ammount = Convert.ToInt32(points);

            DamageSources source = DamageSources.Phisical;
            if (command.Count > 3)
                if (command[3].ToCharArray()[0] > '9')
                    Enum.TryParse<DamageSources>(command[3], true, out source);
            shield.Source = source;

            return result;
        }

        private Action ParseStatusCommand(List<string> command)
        {
            CustomStatusEffect status = new CustomStatusEffect();
            Action result = new Action(status);

            result.Duration = this.GetDuration(command);
            if (result.Duration > 0)
                status.IsTemporary = true;

            result.Target = this.GetTarget(command[0], "status");
            command.RemoveAt(0);

            status.Type = this.GetAlterEffect(command[0], "action");
            command.RemoveAt(0);
            
            if (status.Type != AlterEffectType.Reset)
            {
                command.ForEach(s => status.Description += s + " ");
                status.Description = status.Description.Substring(0, status.Description.Length - 1);
            }
            return result;
        }

        private Action ParseActionCommand(List<string> command)
        {
            ActionEffect combatAction = new ActionEffect();
            Action result = new Action(combatAction);

            result.Target = this.GetTarget(command[0], "action");

            combatAction.Type = this.GetAlterEffect(command[1], "action");

            return result;
        }

        private Unit GetTarget(string target, string operation)
        {
            Unit result = this.GetTarget(target);
            if (result == null)
                throw new ConsoleException(string.Format("{0}: non capisco il bersaglio", operation));
            return result;
        }

        private Unit GetTarget(string target)
        {
            return this.Manager.Units.FirstOrDefault(u => u.Name.ToLower() == target.ToLower());
        }

        private int GetDuration(List<string> command)
        {
            for (int i = command.Count - 1; i >= 0; i--)
            {
                string arg = command[i];
                if (!arg.StartsWith("x")) continue;
                char[] chars = arg.ToCharArray();
                if (chars.Length < 2) return 0;
                if (chars[1] < '1' || chars[1] > '9') return 0;
                string duration = arg.Substring(1, arg.Length - 1);
                command.RemoveAt(i);
                return Convert.ToInt32(duration);
            }
            return 0;
        }

        public AlterEffectType GetAlterEffect(string effect, string operation)
        {
            switch (effect.ToLower())
            {
                case "add":
                    return AlterEffectType.Add;
                case "remove":
                    return AlterEffectType.Remove;
                case "reset":
                    return AlterEffectType.Reset;
                default:
                    throw new ConsoleException(string.Format("{0}: è ammesso solo add, remove e reset", operation));
            }
        }
    }
}
